home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / generic / tclPreserve.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  7.0 KB  |  278 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tclPreserve.c --
  3.  *
  4.  *    This file contains a collection of procedures that are used
  5.  *    to make sure that widget records and other data structures
  6.  *    aren't reallocated when there are nested procedures that
  7.  *    depend on their existence.
  8.  *
  9.  * Copyright (c) 1991-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  *
  15.  * SCCS: @(#) tclPreserve.c 1.18 96/08/05 13:15:08
  16.  */
  17.  
  18. #include "tclInt.h"
  19.  
  20. /*
  21.  * The following data structure is used to keep track of all the
  22.  * Tcl_Preserve calls that are still in effect.  It grows as needed
  23.  * to accommodate any number of calls in effect.
  24.  */
  25.  
  26. typedef struct {
  27.     ClientData clientData;    /* Address of preserved block. */
  28.     int refCount;        /* Number of Tcl_Preserve calls in effect
  29.                  * for block. */
  30.     int mustFree;        /* Non-zero means Tcl_EventuallyFree was
  31.                  * called while a Tcl_Preserve call was in
  32.                  * effect, so the structure must be freed
  33.                  * when refCount becomes zero. */
  34.     Tcl_FreeProc *freeProc;    /* Procedure to call to free. */
  35. } Reference;
  36.  
  37. static Reference *refArray;    /* First in array of references. */
  38. static int spaceAvl = 0;    /* Total number of structures available
  39.                  * at *firstRefPtr. */
  40. static int inUse = 0;        /* Count of structures currently in use
  41.                  * in refArray. */
  42. #define INITIAL_SIZE 2
  43.  
  44. /*
  45.  * Static routines in this file:
  46.  */
  47.  
  48. static void    PreserveExitProc _ANSI_ARGS_((ClientData clientData));
  49.  
  50.  
  51. /*
  52.  *----------------------------------------------------------------------
  53.  *
  54.  * PreserveExitProc --
  55.  *
  56.  *    Called during exit processing to clean up the reference array.
  57.  *
  58.  * Results:
  59.  *    None.
  60.  *
  61.  * Side effects:
  62.  *    Frees the storage of the reference array.
  63.  *
  64.  *----------------------------------------------------------------------
  65.  */
  66.  
  67.     /* ARGSUSED */
  68. static void
  69. PreserveExitProc(clientData)
  70.     ClientData clientData;        /* NULL -Unused. */
  71. {
  72.     if (spaceAvl != 0) {
  73.         ckfree((char *) refArray);
  74.         refArray = (Reference *) NULL;
  75.         inUse = 0;
  76.         spaceAvl = 0;
  77.     }
  78. }
  79.  
  80. /*
  81.  *----------------------------------------------------------------------
  82.  *
  83.  * Tcl_Preserve --
  84.  *
  85.  *    This procedure is used by a procedure to declare its interest
  86.  *    in a particular block of memory, so that the block will not be
  87.  *    reallocated until a matching call to Tcl_Release has been made.
  88.  *
  89.  * Results:
  90.  *    None.
  91.  *
  92.  * Side effects:
  93.  *    Information is retained so that the block of memory will
  94.  *    not be freed until at least the matching call to Tcl_Release.
  95.  *
  96.  *----------------------------------------------------------------------
  97.  */
  98.  
  99. void
  100. Tcl_Preserve(clientData)
  101.     ClientData clientData;    /* Pointer to malloc'ed block of memory. */
  102. {
  103.     Reference *refPtr;
  104.     int i;
  105.  
  106.     /*
  107.      * See if there is already a reference for this pointer.  If so,
  108.      * just increment its reference count.
  109.      */
  110.  
  111.     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
  112.     if (refPtr->clientData == clientData) {
  113.         refPtr->refCount++;
  114.         return;
  115.     }
  116.     }
  117.  
  118.     /*
  119.      * Make a reference array if it doesn't already exist, or make it
  120.      * bigger if it is full.
  121.      */
  122.  
  123.     if (inUse == spaceAvl) {
  124.     if (spaceAvl == 0) {
  125.             Tcl_CreateExitHandler((Tcl_ExitProc *) PreserveExitProc,
  126.                     (ClientData) NULL);
  127.         refArray = (Reference *) ckalloc((unsigned)
  128.             (INITIAL_SIZE*sizeof(Reference)));
  129.         spaceAvl = INITIAL_SIZE;
  130.     } else {
  131.         Reference *new;
  132.  
  133.         new = (Reference *) ckalloc((unsigned)
  134.             (2*spaceAvl*sizeof(Reference)));
  135.         memcpy((VOID *) new, (VOID *) refArray,
  136.                     spaceAvl*sizeof(Reference));
  137.         ckfree((char *) refArray);
  138.         refArray = new;
  139.         spaceAvl *= 2;
  140.     }
  141.     }
  142.  
  143.     /*
  144.      * Make a new entry for the new reference.
  145.      */
  146.  
  147.     refPtr = &refArray[inUse];
  148.     refPtr->clientData = clientData;
  149.     refPtr->refCount = 1;
  150.     refPtr->mustFree = 0;
  151.     refPtr->freeProc = TCL_STATIC;
  152.     inUse += 1;
  153. }
  154.  
  155. /*
  156.  *----------------------------------------------------------------------
  157.  *
  158.  * Tcl_Release --
  159.  *
  160.  *    This procedure is called to cancel a previous call to
  161.  *    Tcl_Preserve, thereby allowing a block of memory to be
  162.  *    freed (if no one else cares about it).
  163.  *
  164.  * Results:
  165.  *    None.
  166.  *
  167.  * Side effects:
  168.  *    If Tcl_EventuallyFree has been called for clientData, and if
  169.  *    no other call to Tcl_Preserve is still in effect, the block of
  170.  *    memory is freed.
  171.  *
  172.  *----------------------------------------------------------------------
  173.  */
  174.  
  175. void
  176. Tcl_Release(clientData)
  177.     ClientData clientData;    /* Pointer to malloc'ed block of memory. */
  178. {
  179.     Reference *refPtr;
  180.     int mustFree;
  181.     Tcl_FreeProc *freeProc;
  182.     int i;
  183.  
  184.     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
  185.     if (refPtr->clientData != clientData) {
  186.         continue;
  187.     }
  188.     refPtr->refCount--;
  189.     if (refPtr->refCount == 0) {
  190.  
  191.             /*
  192.              * Must remove information from the slot before calling freeProc
  193.              * to avoid reentrancy problems if the freeProc calls Tcl_Preserve
  194.              * on the same clientData. Copy down the last reference in the
  195.              * array to overwrite the current slot.
  196.              */
  197.  
  198.             freeProc = refPtr->freeProc;
  199.             mustFree = refPtr->mustFree;
  200.         inUse--;
  201.         if (i < inUse) {
  202.         refArray[i] = refArray[inUse];
  203.         }
  204.         if (mustFree) {
  205.         if ((freeProc == TCL_DYNAMIC) ||
  206.                         (freeProc == (Tcl_FreeProc *) free)) {
  207.             ckfree((char *) clientData);
  208.         } else {
  209.             (*freeProc)((char *) clientData);
  210.         }
  211.         }
  212.     }
  213.     return;
  214.     }
  215.  
  216.     /*
  217.      * Reference not found.  This is a bug in the caller.
  218.      */
  219.  
  220.     panic("Tcl_Release couldn't find reference for 0x%x", clientData);
  221. }
  222.  
  223. /*
  224.  *----------------------------------------------------------------------
  225.  *
  226.  * Tcl_EventuallyFree --
  227.  *
  228.  *    Free up a block of memory, unless a call to Tcl_Preserve is in
  229.  *    effect for that block.  In this case, defer the free until all
  230.  *    calls to Tcl_Preserve have been undone by matching calls to
  231.  *    Tcl_Release.
  232.  *
  233.  * Results:
  234.  *    None.
  235.  *
  236.  * Side effects:
  237.  *    Ptr may be released by calling free().
  238.  *
  239.  *----------------------------------------------------------------------
  240.  */
  241.  
  242. void
  243. Tcl_EventuallyFree(clientData, freeProc)
  244.     ClientData clientData;    /* Pointer to malloc'ed block of memory. */
  245.     Tcl_FreeProc *freeProc;    /* Procedure to actually do free. */
  246. {
  247.     Reference *refPtr;
  248.     int i;
  249.  
  250.     /*
  251.      * See if there is a reference for this pointer.  If so, set its
  252.      * "mustFree" flag (the flag had better not be set already!).
  253.      */
  254.  
  255.     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
  256.     if (refPtr->clientData != clientData) {
  257.         continue;
  258.     }
  259.     if (refPtr->mustFree) {
  260.         panic("Tcl_EventuallyFree called twice for 0x%x\n", clientData);
  261.         }
  262.         refPtr->mustFree = 1;
  263.     refPtr->freeProc = freeProc;
  264.         return;
  265.     }
  266.  
  267.     /*
  268.      * No reference for this block.  Free it now.
  269.      */
  270.  
  271.     if ((freeProc == TCL_DYNAMIC)
  272.         || (freeProc == (Tcl_FreeProc *) free)) {
  273.     ckfree((char *) clientData);
  274.     } else {
  275.     (*freeProc)((char *)clientData);
  276.     }
  277. }
  278.